package com.mymall.serve.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.mymall.serve.common.RootResult;
import com.mymall.serve.config.ThreadConfig;
import com.mymall.serve.config.redission.annotation.Lock;
import com.mymall.serve.config.redission.operation.RedissonObject;
import com.mymall.serve.dto.param.TestParam;
import com.mymall.serve.utils.CacheUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import java.util.Random;
import java.util.concurrent.CountDownLatch;

@Slf4j
@RestController
@RequestMapping("/test")
public class TestController {

    @Autowired
    private RedissonObject redissonObject;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private CacheUtil cacheUtil;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private ThreadConfig threadConfig;

    private static final String VERSIONID = "01";
    private static final String CHRSET = "UTF-8";
    private static final String CUSTOMNAME = "CUBD";
    private static final String TRANSTYPE = "STD_VERI";
    private static final String CUSTID = "6000003635";
    private static final String CUSTSECRET = "e17fc695b17edc43f7d412ba8c531ada";

    @GetMapping("/aaa/hello")
    public RootResult<String> hello() {
        return RootResult.success("http测试");
    }

    @GetMapping("/hello")
    public RootResult<String> hello2() {
        return RootResult.success("http测试2");
    }

    @PostMapping("/postTest")
    @Lock(keys = "#testParam.code",keyConstant = "_testCode")
    public RootResult<String> testLock(@RequestBody TestParam testParam){
        return RootResult.success("success");
    }

    @GetMapping("/testRedisson")
    public RootResult<String> testRedisson() {
        Boolean testRedisson = redissonObject.trySetValue("testRedisson", 1, 5 * 60 * 1000L);
        if (testRedisson) {
            return RootResult.success("加锁成功");
        }
        return RootResult.success("set失败");
    }

    /**
     * 并发测试--测试@Lock
     *
     * @author weiwei
     * @date 2020/4/29 13:09
     */
    @GetMapping("/testConcurrency")
    public void TestConcurrency() {
        final CountDownLatch countDownLatch = new CountDownLatch(1);
        for (int i = 0; i < 5; i++) {
            TestThread myRunnable = new TestThread(countDownLatch, i);
//            Thread myThread = new Thread(myRunnable);
//            myThread.start();
            threadConfig.taskExecutor().execute(myRunnable);
        }
        countDownLatch.countDown();
    }


    public class TestThread implements Runnable {

        private final CountDownLatch startSignal;
        private int threadid;

        public TestThread(CountDownLatch startSignal, Integer threadid) {
            super();
            this.startSignal = startSignal;
            this.threadid = threadid;
        }

        @Override
        public void run() {
            try {
                startSignal.await();
                log.info("thread-{},准备", threadid);
                //一直阻塞当前线程，直到计时器的值为0
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //实际测试操作
            doWork();
        }

        private void doWork() {
            log.info("thread-{},开始执行", threadid);
            /*String jsonStr = "{\n" +
                    "\t\"sign\": \"7e539b26a60007034de7a847fb74a139\",\n" +
                    "\t\"timestamp\": 1605594966626,\n" +
                    "\t\"orderCode\": \"test-003\",\n" +
                    "\t\"orderStartDate\": \"2020-10-01\",\n" +
                    "\t\"orderEndDate\": \"2022-10-01\",\n" +
                    "\t\"orderEffectiveTime\": \"202010\",\n" +
                    "\t\"estimatedAdvertisingMoney\": \"123456.78\",\n" +
                    "\t\"coreCityTotalAdvertising\": 100,\n" +
                    "\t\"focusCityTotalAdvertising\": 100,\n" +
                    "\t\"otherCityTotalAdvertising\": 10,\n" +
                    "\t\"totalAdvertisingUntilMonth\": 202010,\n" +
                    "\t\"accountId\": 1234567,\n" +
                    "\t\"corporationName\": \"测试1\",\n" +
                    "\t\"operator\": \"联通大数据\",\n" +
                    "\t\"customName\": \"客户001\",\n" +
                    "\t\"customIndustry\": \"信息服务业\",\n" +
                    "\t\"customManager\": \"魏巍\",\n" +
                    "\t\"customManagerTel\": \"18600197436\",\n" +
                    "\t\"supporter\": \"魏巍\",\n" +
                    "\t\"supporterTel\": \"18600197436\",\n" +
                    "\t\"advertisingList\": [{\n" +
                    "\t\t\t\"adSpace\": \"moments_core_city\",\n" +
                    "\t\t\t\"settlement\": \"CPM\",\n" +
                    "\t\t\t\"estimatedAmount\": 10000,\n" +
                    "\t\t\t\"advertisingUnitPrice\": \"1.5\"\n" +
                    "\t\t},\n" +
                    "\t\t{\n" +
                    "\t\t\t\"adSpace\": \"moments_important_city\",\n" +
                    "\t\t\t\"settlement\": \"CPM\",\n" +
                    "\t\t\t\"estimatedAmount\": 1000,\n" +
                    "\t\t\t\"advertisingUnitPrice\": \"1\"\n" +
                    "\t\t},\n" +
                    "\t\t{\n" +
                    "\t\t\t\"adSpace\": \"moments_other_city\",\n" +
                    "\t\t\t\"settlement\": \"CPM\",\n" +
                    "\t\t\t\"estimatedAmount\": 101,\n" +
                    "\t\t\t\"advertisingUnitPrice\": \"1\"\n" +
                    "\t\t}\n" +
                    "\t]\n" +
                    "}";
            JSONObject jsonObject = JSON.parseObject(jsonStr);

            ResponseEntity<String> stringResponseEntity = postJsonHttp("http://127.0.0.1:9081/adandorder/boss/sync/pushOrder",
                    jsonObject.toJSONString());
            log.info("result:{}", stringResponseEntity.getBody());*/
            Boolean booleanResult = cacheUtil.setValueIfAbsent("testKey",String.valueOf(threadid),5);
            log.info("thread:{},cacheInsertResult:{}",threadid,booleanResult);
            log.info("thread:{},cacheResult:{}",threadid,cacheUtil.getKey("testKey"));
        }
    }

    @SneakyThrows
    @GetMapping("/testSlidFlow")
    public String testSlidFlow() {
        String key = "slidFlowTest";
        for (int i = 0; i < 30000; i++) {
            /*Long aLong = cacheUtil.slideFlowCount(key, 2);
            log.info("当前窗口内数量{}",aLong);
            Thread.sleep(100);*/
            redisTemplate.opsForZSet().add("test",String.valueOf(System.currentTimeMillis()),System.currentTimeMillis());
        }
        return "success";
    }

    @GetMapping("/test3")
    public String test3() {

        String jsonStr = "{\n" +
                "\t\"sign\": \"7e539b26a60007034de7a847fb74a139\",\n" +
                "\t\"timestamp\": 1605594966626,\n" +
                "\t\"orderCode\": \"test-002\",\n" +
                "\t\"orderStartDate\": \"2020-10-01\",\n" +
                "\t\"orderEndDate\": \"2022-10-01\",\n" +
                "\t\"orderEffectiveTime\": \"202011\",\n" +
                "\t\"estimatedAdvertisingMoney\": \"123456.78\",\n" +
                "\t\"coreCityTotalAdvertising\": 100,\n" +
                "\t\"focusCityTotalAdvertising\": 100,\n" +
                "\t\"otherCityTotalAdvertising\": 10,\n" +
                "\t\"totalAdvertisingUntilMonth\": 202010,\n" +
                "\t\"accountId\": 1234567,\n" +
                "\t\"corporationName\": \"测试1\",\n" +
                "\t\"operator\": \"联通大数据\",\n" +
                "\t\"customName\": \"客户001\",\n" +
                "\t\"customIndustry\": \"信息服务业\",\n" +
                "\t\"customManager\": \"魏巍\",\n" +
                "\t\"customManagerTel\": \"18600197436\",\n" +
                "\t\"supporter\": \"魏巍\",\n" +
                "\t\"supporterTel\": \"18600197436\",\n" +
                "\t\"advertisingList\": [{\n" +
                "\t\t\t\"adSpace\": \"moments_core_city\",\n" +
                "\t\t\t\"settlement\": \"CPM\",\n" +
                "\t\t\t\"estimatedAmount\": 10000,\n" +
                "\t\t\t\"advertisingUnitPrice\": \"1.5\"\n" +
                "\t\t},\n" +
                "\t\t{\n" +
                "\t\t\t\"adSpace\": \"moments_important_city\",\n" +
                "\t\t\t\"settlement\": \"CPC\",\n" +
                "\t\t\t\"estimatedAmount\": 1000,\n" +
                "\t\t\t\"advertisingUnitPrice\": \"1\"\n" +
                "\t\t},\n" +
                "\t\t{\n" +
                "\t\t\t\"adSpace\": \"moments_other_city\",\n" +
                "\t\t\t\"settlement\": \"CPM\",\n" +
                "\t\t\t\"estimatedAmount\": 101,\n" +
                "\t\t\t\"advertisingUnitPrice\": \"1\"\n" +
                "\t\t}\n" +
                "\t]\n" +
                "}";
        JSONObject jsonObject = JSON.parseObject(jsonStr);

        ResponseEntity<String> stringResponseEntity = postJsonHttp("http://127.0.0.1:9081/adandorder/boss/sync/updateOrder",
                jsonObject.toJSONString());
        return stringResponseEntity.getBody();
    }

    public ResponseEntity<String> postJsonHttp(String requestUrl, String requestBody) {
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);

        HttpEntity<String> requestEntity = new HttpEntity<>(requestBody, requestHeaders);
        ResponseEntity<String> responseEntity = null;
        try {
            log.info("restTemplate请求，执行postJsonHttp方法，请求url:{}, json参数：{}, requestEntity请求体:{}", requestUrl, requestBody, requestEntity);
            responseEntity = restTemplate.postForEntity(requestUrl, requestEntity, String.class);
            log.info("restTemplate请求响应结果:{}", responseEntity);
        } catch (RestClientException e) {
            e.printStackTrace();
        }
        return responseEntity;
    }

    public ResponseEntity<String> postFormUrlencodedHttp(String requestUrl, MultiValueMap<String, String> requestBody) {
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<MultiValueMap<String, String>>(requestBody, requestHeaders);
        ResponseEntity<String> responseEntity = null;
        try {
            log.info("restTemplate请求，执行postFormUrlencodedHttp方法，requestUrl:{}, requestEntity:{}", requestUrl, requestEntity);
            responseEntity = restTemplate.postForEntity(requestUrl, requestEntity, String.class);
            log.info("restTemplate响应:{}", responseEntity);
        } catch (Exception e) {
            log.error("请求{}报错:", requestUrl, e);
        }
        return responseEntity;
    }

    public String getRandomIntStr(int length) {
        StringBuilder sb = new StringBuilder();
        Random random = new Random();
        for (int i = 0; i < length; i++) {
            sb.append(random.nextInt(10));
        }
        return sb.toString();
    }

    public static final String pathSeparator = "/";

    public static void main(String[] args) {
        // 实现url模糊匹配
//        String pattern = "/tes*/**/hello";
        String pattern = "/test/**/aaa/hello";
        String path = "/test/hello";
        long l = System.currentTimeMillis();
        boolean b = doMatch(pattern, path, true);
        System.out.println(b);
        System.out.println(System.currentTimeMillis() - l);
    }

    public static boolean doMatch(String pattern, String path, boolean fullMatch) {
        if (path.startsWith(pathSeparator) != pattern.startsWith(pathSeparator)) {
            return false;
        }

        String[] pattDirs = StringUtils.split(pattern, pathSeparator);
        String[] pathDirs = StringUtils.split(path, pathSeparator);

        int pattIdxStart = 0;
        int pattIdxEnd = pattDirs.length - 1;
        int pathIdxStart = 0;
        int pathIdxEnd = pathDirs.length - 1;

        // Match all elements up to the first **
        while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {
            String patDir = pattDirs[pattIdxStart];
            if ("**".equals(patDir)) {
                break;
            }
            if (!matchStrings(patDir, pathDirs[pathIdxStart])) {
                return false;
            }
            pattIdxStart++;
            pathIdxStart++;
        }


        if (pathIdxStart > pathIdxEnd) {
            // pattern比path长，前缀相同
            // Path is exhausted, only match if rest of pattern is * or **'s
            if (pattIdxStart > pattIdxEnd) {
                return (pattern.endsWith(pathSeparator) ?
                        path.endsWith(pathSeparator) : !path.endsWith(pathSeparator));
            }
            if (!fullMatch) {
                return true;
            }
            if (pattIdxStart == pattIdxEnd && pattDirs[pattIdxStart].equals("*") &&
                    path.endsWith(pathSeparator)) {
                return true;
            }
            for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
                if (!pattDirs[i].equals("**")) {
                    return false;
                }
            }
            return true;
        } else if (pattIdxStart > pattIdxEnd) {
            // path比pattern长，前缀相同
            // String not exhausted, but pattern is. Failure.
            return false;
        } else if (!fullMatch && "**".equals(pattDirs[pattIdxStart])) {
            // Path start definitely matches due to "**" part in pattern.
            return true;
        }

        // up to last '**'
        while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {
            String patDir = pattDirs[pattIdxEnd];
            if (patDir.equals("**")) {
                break;
            }
            if (!matchStrings(patDir, pathDirs[pathIdxEnd])) {
                return false;
            }
            pattIdxEnd--;
            pathIdxEnd--;
        }
        if (pathIdxStart > pathIdxEnd) {
            // String is exhausted
            for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
                if (!pattDirs[i].equals("**")) {
                    return false;
                }
            }
            return true;
        }

        while (pattIdxStart != pattIdxEnd && pathIdxStart <= pathIdxEnd) {
            int patIdxTmp = -1;
            for (int i = pattIdxStart + 1; i <= pattIdxEnd; i++) {
                if (pattDirs[i].equals("**")) {
                    patIdxTmp = i;
                    break;
                }
            }
            if (patIdxTmp == pattIdxStart + 1) {
                // '**/**' situation, so skip one
                pattIdxStart++;
                continue;
            }
            // Find the pattern between padIdxStart & padIdxTmp in str between
            // strIdxStart & strIdxEnd
            int patLength = (patIdxTmp - pattIdxStart - 1);
            int strLength = (pathIdxEnd - pathIdxStart + 1);
            int foundIdx = -1;

            strLoop:
            for (int i = 0; i <= strLength - patLength; i++) {
                for (int j = 0; j < patLength; j++) {
                    String subPat = (String) pattDirs[pattIdxStart + j + 1];
                    String subStr = (String) pathDirs[pathIdxStart + i + j];
                    if (!matchStrings(subPat, subStr)) {
                        continue strLoop;
                    }
                }
                foundIdx = pathIdxStart + i;
                break;
            }

            if (foundIdx == -1) {
                return false;
            }

            pattIdxStart = patIdxTmp;
            pathIdxStart = foundIdx + patLength;
        }

        for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
            if (!pattDirs[i].equals("**")) {
                return false;
            }
        }

        return true;
    }

    public static boolean matchStrings(String pattern, String str) {
        char[] patArr = pattern.toCharArray();
        char[] strArr = str.toCharArray();
        int patIdxStart = 0;
        int patIdxEnd = patArr.length - 1;
        int strIdxStart = 0;
        int strIdxEnd = strArr.length - 1;
        char ch;

        boolean containsStar = false;
        for (char aPatArr : patArr) {
            if (aPatArr == '*') {
                containsStar = true;
                break;
            }
        }

        if (!containsStar) {
            // No '*'s, so we make a shortcut
            if (patIdxEnd != strIdxEnd) {
                return false; // Pattern and string do not have the same size
            }
            for (int i = 0; i <= patIdxEnd; i++) {
                ch = patArr[i];
                if (ch != '?') {
                    if (ch != strArr[i]) {
                        return false;// Character mismatch
                    }
                }
            }
            return true; // String matches against pattern
        }


        if (patIdxEnd == 0) {
            return true; // Pattern contains only '*', which matches anything
        }

        // Process characters before first star
        while ((ch = patArr[patIdxStart]) != '*' && strIdxStart <= strIdxEnd) {
            if (ch != '?') {
                if (ch != strArr[strIdxStart]) {
                    return false;// Character mismatch
                }
            }
            patIdxStart++;
            strIdxStart++;
        }
        if (strIdxStart > strIdxEnd) {
            // All characters in the string are used. Check if only '*'s are
            // left in the pattern. If so, we succeeded. Otherwise failure.
            for (int i = patIdxStart; i <= patIdxEnd; i++) {
                if (patArr[i] != '*') {
                    return false;
                }
            }
            return true;
        }

        // Process characters after last star
        while ((ch = patArr[patIdxEnd]) != '*' && strIdxStart <= strIdxEnd) {
            if (ch != '?') {
                if (ch != strArr[strIdxEnd]) {
                    return false;// Character mismatch
                }
            }
            patIdxEnd--;
            strIdxEnd--;
        }
        if (strIdxStart > strIdxEnd) {
            // All characters in the string are used. Check if only '*'s are
            // left in the pattern. If so, we succeeded. Otherwise failure.
            for (int i = patIdxStart; i <= patIdxEnd; i++) {
                if (patArr[i] != '*') {
                    return false;
                }
            }
            return true;
        }

        // process pattern between stars. padIdxStart and patIdxEnd point
        // always to a '*'.
        while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
            int patIdxTmp = -1;
            for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
                if (patArr[i] == '*') {
                    patIdxTmp = i;
                    break;
                }
            }
            if (patIdxTmp == patIdxStart + 1) {
                // Two stars next to each other, skip the first one.
                patIdxStart++;
                continue;
            }
            // Find the pattern between padIdxStart & padIdxTmp in str between
            // strIdxStart & strIdxEnd
            int patLength = (patIdxTmp - patIdxStart - 1);
            int strLength = (strIdxEnd - strIdxStart + 1);
            int foundIdx = -1;
            strLoop:
            for (int i = 0; i <= strLength - patLength; i++) {
                for (int j = 0; j < patLength; j++) {
                    ch = patArr[patIdxStart + j + 1];
                    if (ch != '?') {
                        if (ch != strArr[strIdxStart + i + j]) {
                            continue strLoop;
                        }
                    }
                }

                foundIdx = strIdxStart + i;
                break;
            }

            if (foundIdx == -1) {
                return false;
            }

            patIdxStart = patIdxTmp;
            strIdxStart = foundIdx + patLength;
        }

        // All characters in the string are used. Check if only '*'s are left
        // in the pattern. If so, we succeeded. Otherwise failure.
        for (int i = patIdxStart; i <= patIdxEnd; i++) {
            if (patArr[i] != '*') {
                return false;
            }
        }

        return true;
    }

}
